#include "Julianna.h"

// #include "Lily_help.h"

#define Hold_time_delay 80 //1s trig long press
#define Normal_delay 10
int Press_delay = 100;
int Min_press_delay = 20;
#define ABS(x) ((x) > 0 ? (x) : -(x))
#define MOVE_min_pos 3

volatile JUI _this = NULL;
// List<JUI>
Li_List Julianna = NULL; //控件管理, list of
JUI _juli_last_activity = NULL;

int _xoff = 0, _yoff = 0; // 坐标偏移
// float _xsale = 1, _ysale = 1; // 尺度缩放

CursorItem cursor_current_item = NULL;
void init_julianna()
{
	//Julianna = new_list(sizeof(UI_compent_t), 8);
	Julianna = new_list(sizeof(JUI), 8);
	if (Julianna == NULL)
	{
#ifdef in_debug
		lily_out("Julianna init failed 0x27");
#endif // in_debug
		return;
	}
	//public_a_var_ref("_hold_delay", &Press_delay, 'd');
	//public_a_var_ref("_mhd", &Min_press_delay, 'd');

	int n = public_a_cmd_link("juli", cmd_juli);
	Cmd cmd = li_cmds + n;
	// juli-s x y w h
	// juli-update
	// -circle x y r [c]
	// -rec x y w d
	// -line x y xe ye

	cmd->annotation = "juli control\n-update\n-size index x y w h\n-hide index [val]"
					  "-c[fill] x y r [color]\n-r[f] x y w h [c]\n-line xs ys xe ye";
}

JUI public_a_ui_compent_id(int x, int y, int width, int height, pint id, OnTouching hook, OnDrawing draw)
{
	//UI_compent_t ui = {x, y, width, height, id, hook, draw};
	JUI ui = malloc(sizeof(UI_compent_t));
	if (!ui)
		return NULL;
	ui->x = x;
	ui->y = y;
	ui->width = width;
	ui->height = height;
	ui->id = id;
	ui->onDrawing = draw;
	ui->onTouching = hook;
	ui->items = NULL;
	ui->hide = 0;
	if (li_add(Julianna, ui) < 0)
	{
		return NULL;
	}
	//int n = Julianna->count;
	//JUI uis = list_content(Julianna, UI_compent_t);
	return ui; // uis + n - 1;
}

// change _xoff, _yoff, _this as well
// return touch handle, NULL if hit nothing
int _xpar = 0, _ypar = 0;
JUI _parent = NULL;
OnTouching hit_test(int x, int y)
{
	int i;
	i = Julianna->count - 1;
	JUI *uis = list_content(Julianna, JUI); // UI_compent_t);
	_parent = NULL;
	for (; i >= 0; i--)
	{
		_this = uis[i]; // +i;
		if (_this->hide || !(_this->onTouching))
			continue;
		if (x < _this->x || x > _this->x + _this->width || y < _this->y || y > _this->y + _this->height)
			continue;
		OnTouching touch = _this->onTouching;
		x -= _this->x;
		y -= _this->y;
		_xoff = _this->x;
		_yoff = _this->y;
		_parent = _this;
		// hit OK
		if (_this->items)
		{
			
			Layout_item items = list_content(_this->items, Layout_item_t);
			int n = _this->items->count;
			int j;
			for (j = 0; j < n; j++)
			{
				_this = cast(items + j, JUI);
				if (x < _this->x || x > _this->x + _this->width || y < _this->y || y > _this->y + _this->height)
					continue;
				_xoff += _this->x;
				_yoff += _this->y;
				// hit sub items
				return touch;
			}
			// hit a ui, but no item
			// equal hit nothing
			_this = NULL;
			_parent=NULL;
			return NULL;
		}
		// hit a ui
		return touch;
	}
	// hit no ui
	_this = NULL;
	_parent = NULL;
	return NULL;
}

int juli_boardcast_event(int x, int y, Presse_Statue event)
{
	static int long_press_time = 0;
	static int lx = 0, ly = 0;
	static OnTouching last_touch = NULL;
	int delay_time = Normal_delay;
	JUI call = NULL;
	OnTouching touch = hit_test(x, y);
	x -= _xoff;
	y -= _yoff;
	// save data
	int ba[] = {_xoff, _yoff};
	JUI ba_this = _this;
	switch (event)
	{
	case PressedDown:
		if (_this)
		{
			call = touch(_this, x, y, event);
			long_press_time = 0;
		}
		break;
	case PressedUp:
		if (_this)
		{
			call = touch(_this, x, y, event);
			delay_time = Press_delay;
		}
		break;
	case Moving:
		if (_this)
		{
			if (_this != _juli_last_activity)
			{
				if (_juli_last_activity) //leave this
				{
					// note here:
					// cause x,y have changed and it's call last UI, it's not supposed to
					JUI u = _this;
					// use last act and location
					_this = _juli_last_activity;
					int x0 = x, y0 = y;
					int xof = _xoff, yof = _yoff;
					x += _xoff - _xpar;
					y += _yoff - _ypar;
					_xoff = _xpar;
					_yoff = _ypar;
					// touch = last_touch;
					call = last_touch(_this, x, y, MoveOut);
					//resume x, y
					x = x0;
					x = y0;
					_xoff = xof;
					_yoff = yof;
					_this = u;
				}
				//enter  there
				long_press_time = 0; //开始计时
				call = touch(_this, x, y, MoveIn);
			}
			else //same ui, move or long press
			{
				lx -= x;
				ly -= y;
				if (ABS(lx) > MOVE_min_pos || ABS(ly) > MOVE_min_pos)
				{
					call = touch(_this, x, y, Moving);
				}
				else
				{
					//长按触发
					long_press_time++;
					if (long_press_time > Hold_time_delay)
					{
						call = touch(_this, x, y, Pressed);

						int t = Press_delay;
						t = t - (long_press_time - Hold_time_delay) * 4.0f;
						if (t < Min_press_delay)
							t = Min_press_delay;
						delay_time = t;
					}
				}
			}
		}
		else
		{
			if (_juli_last_activity) // leave
			{
				_this = _juli_last_activity;
				// touch = last_touch;
				x += _xoff - _xpar;
				y += _yoff - _ypar;
				_xoff = _xpar;
				_yoff = _ypar;
				call = last_touch(_this, x, y, MoveOut);
				_this = NULL;
			}
		}
		break;
	default:
		break;
	}
	_juli_last_activity = ba_this;					   // _this;
	last_touch = ba_this ? _parent->onTouching : NULL; // touch, cause onTOuch may change
	// _juli_last_activity = _this;					   // _this;
	// last_touch = touch; // touch, cause onTOuch may change

	lx = x;
	ly = y;
	_xpar = *ba;   //_xoff; // parent location
	_ypar = ba[1]; // _yoff;

	if (call != NULL)
	{
		juli_show_ui(call, ByPass);
	}

	return delay_time;
}

void juli_show_up()
{
	LCD_Clear(BACK_COLOR);
	int n = Julianna->count;
	JUI *uis = list_content(Julianna, JUI);
	int i;
	for (i = 0; i < n; i++)
	{
		juli_show_ui(uis[i], OnCreate);
	}
}
// depracted use juli_show_ui instand
void juli_show_compent(pint id, ViewEvent events)
{
	int n = Julianna->count;

	JUI *uis = list_content(Julianna, JUI);
	int i;
	for (i = 0; i < n; i++)
	{
		_this = uis[i];
		if (_this->id != id)
			continue;
		juli_show_coment_index(i, events);
	}
}
void juli_show_coment_index(int i, ViewEvent events)
{
	JUI *uis = list_content(Julianna, JUI);
	//_this = &(uis[i]);
	juli_show_ui(uis[i], events);
}

void juli_show_ui(JUI ui, ViewEvent events)
{
	if (ui->hide)
		return;
	_this = ui;
	_xoff = _this->x;
	_yoff = _this->y;
	_this->onDrawing(_this, events);
	if (_this->items)
	{
		OnDrawing draw = _this->onDrawing;
		Layout_item items = list_content(_this->items, Layout_item_t);
		int n = _this->items->count;
		int j;
		int x0 = _xoff;
		int y0 = _yoff;
		for (j = 0; j < n; j++)
		{
			_this = cast(items + j, JUI);
			_xoff = x0 + _this->x;
			_yoff = y0 + _this->y;
			draw(_this, events); // note: only basic member in this is validate
		}
	}
}
JUI new_ui(Gem gem, pint id, OnTouching onTouch, OnDrawing onDraw)
{

	//UI_compent_t ui_t = {*gem, id, onTouch, onDraw, NULL};
	JUI ui = malloc(sizeof(UI_compent_t));
	if (!ui)
	{
		return NULL;
	}
	//memcpy(ui, &ui_t, sizeof(UI_compent_t));
	ui->gem = *gem;
	ui->id = id;
	ui->onDrawing = onDraw;
	ui->onTouching = onTouch;
	ui->items = NULL;
	ui->hide = 0;
	if (li_add(Julianna, ui) < 0)
	{
#ifdef in_debug
		lily_out("mem er -0x13875");
#endif // in_debug
		return NULL;
	}
	//int n = Julianna->count;
	//JUI uis = list_content(Julianna, UI_compent_t);
	return ui; // uis + n - 1;
}
// to delete a ui
// this will free the memory
int remove_ui(JUI ui)
{
	free(ui);
	return list_remove(Julianna, &ui);
}

Layout_item new_item_to(JUI layout, Gem gem, pint id)
{
	if (layout->items == NULL)
	{
		layout->items = new_list(sizeof(Layout_item_t), 4);
		if (layout->items == NULL)
		{
#ifdef in_debug
			lily_out("mem er -0x1387");
#endif // in_debug
			return NULL;
		}
	}
	Layout_item_t ui = {*gem, id};
	if (li_add(layout->items, ui) < 0)
	{
#ifdef in_debug
		lily_out("mem er -0x13878");
#endif // in_debug
		return NULL;
	}
	Layout_item uis = list_content(layout->items, Layout_item_t);
	int n = layout->items->count;
	return uis + n - 1;
}

void Juli_fill_round_rect(int x, int y, int w, int h, int r, int c)
{
	x += _xoff;
	y += _yoff;
	int x2 = x + w, y2 = y + h;
	ug_FillRoundFrame(x, y, x2, y2, r, c);
}
void Juli_draw_round_rect(int x, int y, int w, int h, int r)
{
	x += _xoff;
	y += _yoff;
	int x2 = x + w;
	int y2 = y + h;
	ug_DrawRoundFrame(x, y, x2, y2, r);
}

// juli-show
// juli-s x y w h
// juli-update
// -circle x y r [c]
// -rec x y w d
// -line x y xe ye
int rgb888_torgb565(int co)
{
	int r, g, b;
	unsigned char *c = (unsigned char *)&co;
	b = c[0];
	g = c[1];
	r = c[2];
	// &0x1f;
	b = b >> 3;
	g >>= 2;
	r >>= 3;
	return (r << 11) | (g << 5) | b;
}

const char *cmd_juli_annotation = "juli control\n"
								  "-update\n"
								  "-size index [x y w h][margain]\n"
								  "-hide index [val]"
								  "-c[fill] x y r [color]\n"
								  "-r[f] x y w h [c]\n"
								  "-line xs ys xe ye";
int cmd_juli(int n, char *arg[])
{
	char mode = arg[0][5];

	if (!arg[0][4])
	{
		if (n == 2)
		{
			if (isD(arg[1][0]))
			{
				n = arg[1][0] - '0';
				JUI *uis = list_content(Julianna, JUI);
				if (str_is_name(uis[n]->id))
				{
					lily_out(uis[n]->id);
				}
				else
				{
					sprintf(tx, "%p", uis[n]->id);
					lily_out(tx);
				}
				sprintf(tx, " H:%d", (int)(uis[n]->hide));
				lily_out(tx);
				sprintf(tx, " G:%d,%d,%d,%d\n", uis[n]->x, uis[n]->y, uis[n]->width, uis[n]->height);
				lily_out(tx);
				return uis[n]->height;
			}
		}
		n = Julianna->count;
		JUI *uis = list_content(Julianna, JUI);
		int i;
		for (i = 0; i < n; i++)
		{
			sprintf(tx, "%d):", i);
			lily_out(tx);
			if (str_is_name(uis[i]->id))
			{
				lily_out(uis[i]->id);
			}
			else
			{
				sprintf(tx, "%p", uis[i]->id);
				lily_out(tx);
			}
			sprintf(tx, " H:%d", (int)(uis[i]->hide));
			lily_out(tx);
			sprintf(tx, " G:%d,%d,%d,%d\n", uis[i]->x, uis[i]->y, uis[i]->width, uis[i]->height);
			lily_out(tx);
		}
		return n;
	}
	if (mode == 'u')
	{
		juli_show_up();
		return Julianna->count;
	}
	if (mode == 's') // -resize index x y w h
	{
		if (!str_is_numeric(arg[1]))
		{
			li_error("-resize index x y w h", -376);
		}

		int i = atoi(arg[1]);
		JUI ui = (list_content(Julianna, JUI))[i];
		if (n != 6)
		{
			int mx = 2, my = 2;
			if (n > 2)
			{
				mx = atoi(arg[2]);
				if (mx < 1)
					mx = 2;
			}
			if (n > 3)
			{
				my = atoi(arg[3]);
				if (my < 1)
					my = 2;
			}
			else
				my = mx;
			window_expand(ui, mx, my);
			juli_show_up();
			return 0;
		}
		for (i = 0; i < 4; i++)
		{
			n = 0;
			if (arg[i + 2][0] == '-' || arg[i + 2][0] == '+')
			{
				n = ((int *)(&(ui->gem)))[i];
				if (!str_is_numeric(arg[i + 2] + 1))
					continue;
			}
			else if (!str_is_numeric(arg[i + 2]))
				continue;
			n += atoi(arg[i + 2]);
			((int *)(&(ui->gem)))[i] = n;
		}
		juli_show_up();
		return i;
	}
	if (mode == 'h') //hide index
	{
		int ind = 0;
		assert_msg(n >= 2, "bad arg");
		if (!str_is_numeric(arg[1]))
		{
			li_error("bad index", -__LINE__);
		}
		ind = atoi(arg[1]);
		assert_msg(ind < Julianna->count, "index over");
		int val = 1;
		if (n >= 3)
		{
			val = arg[2][0] == '1';
		}
		(list_content(Julianna, JUI)[ind])->hide = val;
		return val;
	}

	int i;
	int x, y, c = POINT_COLOR;
	if (arg[0][6] != '\0') // -xx : to fill; -x to draw
	{
		i = n - 1;
		if (!str_is_numeric(arg[i]))
		{
			if (arg[i][0] == 'r')
				c = Color_red;
			else if (arg[i][0] == 'g')
				c = Color_green;
			else if (arg[i][0] == 'b')
				c = Color_blue;
			else if (arg[i][0] == 'g')
				c = Color_green;
			else if (arg[i][0] == 'y')
				c = Color_yellow;
			else if (arg[i][0] == 'd')
				c = Color_black;
			else if (arg[i][0] == 'w')
				c = Color_white;
			else
			{
				int ok = sscanf(arg[n - 1], "%x", &c);
				if (!ok)
				{
					li_error("color error", -__LINE__);
				}
				c = rgb888_torgb565(c);
			}

			n--;
		}
	}

	if (n < 3)
	{
		li_error("arg missing", -__LINE__);
	}
	for (i = 1; i < n; i++)
	{
		if (!str_is_numeric(arg[i]))
		{
			li_error("not numberic", -__LINE__);
		}
	}
	x = atoi(arg[1]);
	y = atoi(arg[2]);

	if (mode == 'c') //circle x y r
	{
		int r = 1;
		if (n >= 4)
		{
			r = atoi(arg[3]);
		}
		if (arg[0][6] != '\0')
		{
			ug_FillCircle(x, y, r, c);
		}
		else
			LCD_Draw_Circle(x, y, r);
		return 0;
	}
	if (mode == 'r') //rectangle
	{
		if (n < 5)
		{
			li_error("arg missing", -__LINE__);
		}
		int w = atoi(arg[3]);
		int h = atoi(arg[4]);
		if (arg[0][6] != '\0')
		{
			LCD_fill_with(x, y, w, h, c);
		}
		else
			LCD_DrawRectangle(x, y, x + w, y + h);
		return 0;
	}

	if (mode == 'l') //line
	{
		if (n < 5)
		{
			li_error("arg missing", -__LINE__);
		}
		int x2 = atoi(arg[3]);
		int y2 = atoi(arg[4]);
		LCD_DrawLine(x, y, x2, y2);
		return 0;
	}
	li_error("-list,-update,-resize", -1);
}

void cursor_push_in(void *arg, TasksArg_def task, void (*loseFocusCallBack)(void *arg))
{
	if (!cursor_current_item)
	{
		cursor_current_item = malloc(sizeof(CursorItem_t));
		if (!cursor_current_item)
			return;
	}
	else
	{
		// tell the last ui to lose foucs
		cursor_current_item->loseFocusCallBack(cursor_current_item->arg);
		// delete the last shine Timer
		remove_timerArg(cursor_current_item->shineTask, cursor_current_item->arg);
	}
	// then add new shine timer with new args
	cursor_current_item->arg = arg;
	cursor_current_item->shineTask = task;
	cursor_current_item->loseFocusCallBack = loseFocusCallBack;
	public_a_timerArg(task, Hz(2), arg);
}
void delete_cursorItem()
{
	if (!cursor_current_item)
		return;
	remove_timerArg(cursor_current_item->shineTask, cursor_current_item->arg);
	free(cursor_current_item);
	cursor_current_item = NULL;
}
int delete_if_its_your_cursor(void *arg, TasksArg_def task)
{
	if (!cursor_current_item)
		return 0;
	if (cursor_current_item->arg == arg && cursor_current_item->shineTask == task)
	{
		delete_cursorItem();
		return 1;
	}
	return 0;
}

//to split a window using a mode
// "RowColNum"
int window_split(Gem window, char *split, Gem out_gem, int margain)
{
	int r, c, n;
	// check
	int i;
	for (i = 0; i < 3; i++)
	{
		if (!isD(split[i]))
			return -1;
	}
	r = split[0] - '0';
	c = split[1] - '0';
	n = split[2] - '0';
	int wo = window->width / c;
	int ho = window->height / r;
	// determine the x y
	int x, y;
	x = wo * (n % c - 1);
	if (x < 0)
		x = wo * (c - 1);
	i = c;
	y = 0;
	while (i < n)
	{
		i += c;
		y++;
	}
	y *= ho;
	out_gem->x = x + window->x;
	out_gem->y = y + window->y;
	out_gem->width = wo;
	out_gem->height = ho;

	// the parent windows
	if (r * c == 2)
	{
		if (n == 1)
		{
			n = 2;
			// comes again
			x = wo * (n % c - 1);
			if (x < 0)
				x = wo * (c - 1);
			i = c;
			y = 0;
			while (i < n)
			{
				i += c;
				y++;
			}
			y *= ho;
			if (x)
			{
				window->x += x + margain;
				wo -= margain;
			}
			else
			{
				window->y += y + margain;
				ho -= margain;
			}
			// window->x += x ? x + margain : 0;
			// window->y += y ? y + margain : 0;
		}
		else // parent no change
		{
			if (x)
			{
				out_gem->x += margain;
				out_gem->width -= margain;
			}
			else
			{
				out_gem->y += margain;
				out_gem->height -= margain;
			}
		}
		// else
		// {
		window->width = wo;
		window->height = ho;
		// }
	}
	return 0;
}
void window_expand(JUI window_ui, int my, int mx)
{
	window_expand_y(window_ui, my);
	window_expand_x(window_ui, mx);
}
void window_expand_y(JUI window_ui, int margain)
{
	Gem window = &(window_ui->gem);
	int x, y, w, h;
	x = window->x;
	y = window->y;
	h = window->height;
	w = window->width;
	int n = Julianna->count;
	JUI *uis = list_content(Julianna, JUI);
	// first expand y and height
	int i;
	int min_y2 = Screen_height - 2, max_y1 = 2; // margin 2
	int yh = y + h;
	for (i = 0; i < n; i++)
	{
		JUI ui = uis[i];
		if (ui->x + ui->width < x || ui->x > x + w)
			continue;
		if (window_ui == ui)
			continue;
		int y2 = ui->y + ui->height;
		if (y2 < y)
		{
			if (y2 > max_y1)
				max_y1 = y2;
		}

		if (ui->y > yh)
		{
			if (ui->y < min_y2)
				min_y2 = ui->y;
		}
	}
	y = max_y1 + margain;
	h = min_y2 - y - margain;
	window->y = y;
	window->height = h;
}
void window_expand_x(JUI window_ui, int margain)
{
	Gem window = &(window_ui->gem);
	int x, y, w, h;
	x = window->x;
	y = window->y;
	h = window->height;
	w = window->width;
	int n = Julianna->count;
	JUI *uis = list_content(Julianna, JUI);
	// first expand y and height
	int i;
	int min_x2 = Screen_width - 2, max_x1 = 2; // margin 2
	int xw = x + w;

	for (i = 0; i < n; i++)
	{
		JUI ui = uis[i];
		if (ui->y + ui->height < y || ui->y > y + h)
			continue;
		if (window_ui == ui)
			continue;
		int x2 = ui->x + ui->width;
		if (x2 < x)
		{
			if (x2 > max_x1)
				max_x1 = x2;
		}

		if (ui->x > xw)
		{
			if (ui->x < min_x2)
				min_x2 = ui->x;
		}
	}
	x = max_x1 + margain;
	w = min_x2 - x - margain;
	window->x = x;
	window->width = w;
}

// void let_juli_call(int id)
// {
//     calls[call_num++] = id;
// }
/*

cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D INSTALL_C_EXAMPLES=ON \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D OPENCV_EXTRA_MODULES_PATH=/home/pi/opencv_contrib-3.4.3/modules \
-D BUILD_EXAMPLES=ON \
-D WITH_LIBV4L=ON \
-D PYTHON3_EXECUTABLE=/usr/bin/python3.7 \
-D PYTHON_INCLUDE_DIR=/usr/include/python3.7 \
-D PYTHON_LIBRARY=/usr/lib/arm-linux-gnueabihf/libpython3.7m.so \
-D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include \
..

cmake -D CMAKE_BUILD_TYPE=RELEASE
-D CMAKE_INSTALL_PREFIX=/usr/local
-D INSTALL_C_EXAMPLES=ON
-D INSTALL_PYTHON_EXAMPLES=ON
-D OPENCV_EXTRA_MODULES_PATH=/home/pi/Downloads/opencv_contrib-3.4.0/modules
-D BUILD_EXAMPLES=ON
-D WITH_LIBV4L=ON PYTHON3_EXECUTABLE=/usr/bin/python3.5
PYTHON_INCLUDE_DIR=/usr/include/python3.5
PYTHON_LIBRARY=/usr/lib/arm-linux-gnueabihf/libpython3.5m.so
PYTHON3_NUMPY_INCLUDE_DIRS=/home/pi/.local/lib/python3.5/site-packages/numpy/core/include ..
*/